﻿using System.Linq.Expressions;
using Devonline.AspNetCore;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;

namespace Devonline.Database.NoSQL;

/// <summary>
/// NoSQL 数据库服务的默认抽象实现
/// </summary>
public abstract class NoSQLDataService : NoSQLService, INoSQLDataService, INoSQLService
{
    /// <summary>
    /// Http Context
    /// </summary>
    protected readonly HttpContext _httpContext;
    /// <summary>
    /// Http Request
    /// </summary>
    protected readonly HttpRequest _request;
    /// <summary>
    /// Http 配置
    /// </summary>
    protected readonly HttpSetting _httpSetting;

    /// <summary>
    /// 构造方法
    /// </summary>
    /// <param name="logger"></param>
    /// <param name="endpoint"></param>
    /// <param name="httpContextAccessor"></param>
    /// <param name="httpSetting"></param>
    public NoSQLDataService(ILogger<NoSQLDataService> logger, IDatabaseEndpoint endpoint, IHttpContextAccessor httpContextAccessor, HttpSetting httpSetting) : base(logger, endpoint)
    {
        _httpContext = httpContextAccessor.HttpContext!;
        _request = httpContextAccessor.HttpContext!.Request;
        _httpSetting = httpSetting;
        UserName = GetUserName();
        DataIsolate = httpSetting.DataIsolate;
        DataIsolateId = GetDataIsolateId();
    }

    /// <summary>
    /// 当前登录的用户编号
    /// </summary>
    public virtual string UserId => GetUserId() ?? throw new UnauthorizedAccessException();
    /// <summary>
    /// 当前登录的用户
    /// </summary>
    public virtual string UserName { get; }
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 sub; 在 request 中为 userId
    /// 用户尚未登录系统时抛出 UnauthorizedAccessException 异常
    /// </summary>
    /// <returns></returns>
    /// <exception cref="UnauthorizedAccessException">用户尚未认证访问异常</exception>
    public virtual string? GetUserId() => _httpContext.GetUserId<string>();
    /// <summary>
    /// 从 httpContext 获取用户标识, 用户标识在 User.Claims 中的 type 为 userName
    /// </summary>
    /// <returns></returns>
    public virtual string GetUserName() => _httpContext.GetUserName();
    /// <summary>
    /// 获取数据隔离的数据编号
    /// </summary>
    /// <returns></returns>
    public virtual string? GetDataIsolateId() => _httpContext.GetClaimValue<string>(AppSettings.CLAIM_TYPE_DATA_ISOLATE_ID);

    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <returns></returns>
    public virtual async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>() where TModel : class, new() => await _request.GetPagedResultAsync<TModel>(await GetQueryableAsync<TModel>());
    /// <summary>
    /// 从数据库查询多行数据
    /// </summary>
    /// <typeparam name="TModel">数据对象模型的类型</typeparam>
    /// <param name="predicate">查询表达式</param>
    /// <returns></returns>
    public virtual async Task<PagedResult<TModel>> GetPagedResultAsync<TModel>(Expression<Func<TModel, bool>> predicate) where TModel : class, new() => await _request.GetPagedResultAsync<TModel>(await GetQueryableAsync<TModel>(predicate));

    /// <summary>
    /// 执行循环调用
    /// </summary>
    /// <param name="action">要执行的具体写入方法</param>
    /// <returns></returns>
    protected override async Task<bool> ExecuteAsync(Func<Task> action)
    {
        var retry = 0;
        var success = false;
        var userName = _httpContext.GetUserName();
        while (!success && retry++ <= _endpoint.Retry)
        {
            try
            {
                _logger.LogDebug("用户 {userName} 将操作 {host} 的数据库 {database}", userName, _endpoint.Host, _endpoint.Database);
                await action();
                _logger.LogInformation("用户 {userName} 已操作 {host} 的数据库 {host}", userName, _endpoint.Host, _endpoint.Database);
                success = true;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "用户 {userName} 将数据操作 {host} 的数据库 {host} 时出错!", userName, _endpoint.Host, _endpoint.Database);
            }
        }

        if (!success)
        {
            _logger.LogError("用户 {userName} 将数据操作 {host} 的数据库 {host} 时失败, 且经过 {retry} 次重新尝试均不能成功, 本次操作操作请检查问题后再做处理!", userName, _endpoint.Host, _endpoint.Database, _endpoint.Retry);
        }

        return success;
    }
}